package societyGames
{
import com.societyGames.codeAlong.codeExercise.Compiler;
import com.societyGames.codeAlong.state.codeExercise.model.CaseData;
import com.societyGames.codeAlong.state.codeExercise.model.TestCaseStatus;
import com.societyGames.codeAlong.state.codeExercise.model.testCase.TestCase;
import com.societyGames.system.FileCache;

import flash.events.Event;
import flash.utils.ByteArray;

import org.flexunit.Assert;
import org.flexunit.async.Async;

public class TestCaseTest
{
  private var _testCase:TestCase;
  private const timeout:int = 5000;
  private const longTimeOut:int = 60000;

  [Before(async)]
  public function setup():void
  {
    Async.proceedOnEvent(this, Compiler.instance, Compiler.instance.initializedEventObject.type, timeout);
    Compiler.instance.initialize();
  }

  [Test(async)]
  public function simpleSuccess():void
  {
    var fileCache:FileCache = new FileCache();
    var caseXML:XML =
            <case swf="./../../UnitTest/UnitTestExercise.swf" class="com.societyGames.unitTestExercises.UnitTestCodeExercise"/>;
    var caseData:CaseData = CaseData.fromXml(caseXML);
    _testCase = new TestCase(fileCache, 0, caseData);
    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleSuccessWaitToExecute, timeout);
  }

  private function onSimpleSuccessWaitToExecute(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (!_testCase.canExecute)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleSuccessWaitToExecute, timeout);
      return;
    }

    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleSuccessWaitForSuccess, timeout);
    var executeByteArray:ByteArray = Compiler.instance.compileSwf("setCodeExerciseSuccess(true); __end__();", "UnitTest");
    _testCase.execute(executeByteArray);
  }

  private function onSimpleSuccessWaitForSuccess(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (_testCase.testCaseStatus == TestCaseStatus.executing)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleSuccessWaitForSuccess, timeout);
      return;
    }
    Assert.assertEquals(TestCaseStatus.succeeded, _testCase.testCaseStatus);
  }

  [Test(async)]
  public function simpleFail():void
  {
    var fileCache:FileCache = new FileCache();
    var caseXML:XML =
            <case swf="./../../UnitTest/UnitTestExercise.swf" class="com.societyGames.unitTestExercises.UnitTestCodeExercise"/>;
    var caseData:CaseData = CaseData.fromXml(caseXML);
    _testCase = new TestCase(fileCache, 0, caseData);
    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleFailWaitToExecute, timeout);
  }

  private function onSimpleFailWaitToExecute(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (!_testCase.canExecute)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleFailWaitToExecute, timeout);
      return;
    }

    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleFailWaitForFail, timeout);
    var executeByteArray:ByteArray = Compiler.instance.compileSwf("setCodeExerciseSuccess(false); __end__();", "UnitTest");
    _testCase.execute(executeByteArray);
  }

  private function onSimpleFailWaitForFail(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (_testCase.testCaseStatus == TestCaseStatus.executing)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleFailWaitForFail, timeout);
      return;
    }
    Assert.assertEquals(TestCaseStatus.failed, _testCase.testCaseStatus);
  }

  [Test(async)]
  public function simpleError():void
  {
    var fileCache:FileCache = new FileCache();
    var caseXML:XML =
            <case swf="./../../UnitTest/UnitTestExercise.swf" class="com.societyGames.unitTestExercises.UnitTestCodeExercise"/>;
    var caseData:CaseData = CaseData.fromXml(caseXML);
    _testCase = new TestCase(fileCache, 0, caseData);
    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleErrorWaitToExecute, timeout);
  }

  private function onSimpleErrorWaitToExecute(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (!_testCase.canExecute)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleErrorWaitToExecute, timeout);
      return;
    }

    Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleErrorWaitForFail, timeout);

    var executeByteArray:ByteArray = Compiler.instance.compileSwf(" use namespace 'com.societyGames.codeAlong.userUtility'; function main():void {" +
            "setCodeExerciseSuccess(true); throw new Error('simpleError');" +
            "}; try { main(); } catch (error:Error) { reportException(error); } __end__();"
            , "UnitTest");
    _testCase.execute(executeByteArray);
  }

  private function onSimpleErrorWaitForFail(event:Event, passThroughData:Object):void
  {
    assertStatusMatchesCanGetters();
    if (_testCase.testCaseStatus == TestCaseStatus.executing)
    {
      Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleErrorWaitForFail, timeout);
      return;
    }
    Assert.assertEquals(TestCaseStatus.exceptioned, _testCase.testCaseStatus);
    Assert.assertEquals(_testCase.error.message, "simpleError");
  }

  /*[Test(async)]
   public function simpleUnhandledError():void
   {
   var fileCache:FileCache = new FileCache();
   var caseXML:XML =
   <case swf="./../../UnitTest/UnitTestExercise.swf" class="com.societyGames.unitTestExercises.UnitTestCodeExercise"/>;
   var caseData:CaseData = CaseData.fromXml(caseXML);
   _testCase = new TestCase(fileCache, 0, caseData);
   Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleUnhandledErrorWaitToExecute, longTimeOut);
   }

   private function onSimpleUnhandledErrorWaitToExecute(event:Event, passThroughData:Object):void
   {
   assertStatusMatchesCanGetters();
   if (!_testCase.canExecute)
   {
   Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleUnhandledErrorWaitToExecute, longTimeOut);
   return;
   }

   Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleUnhandledErrorWaitForFail, longTimeOut);

   var executeByteArray:ByteArray = Compiler.instance.compileSwf(" use namespace 'com.societyGames.codeAlong.userUtility'; function main():void {" +
   "setCodeExerciseSuccess(true); throw new Error('unhandledError');" +
   "}; main(); __end__();"
   , "UnitTest");
   _testCase.execute(executeByteArray);
   }

   private function onSimpleUnhandledErrorWaitForFail(event:Event, passThroughData:Object):void
   {
   assertStatusMatchesCanGetters();
   if (_testCase.testCaseStatus == TestCaseStatus.executing)
   {
   Async.handleEvent(this, _testCase, _testCase.testCaseStatusChangedEventObject.type, onSimpleUnhandledErrorWaitForFail, timeout);
   return;
   }
   Assert.assertEquals(TestCaseStatus.unhandledExceptioned, _testCase.testCaseStatus);
   }*/

  private function assertStatusMatchesCanGetters():void
  {
    switch (_testCase.testCaseStatus)
    {
      case null:
        Assert.assertFalse(_testCase.canExecute);
        Assert.assertFalse(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.loadingCodeExercise:
        Assert.assertFalse(_testCase.canExecute);
        Assert.assertFalse(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.resetting:
        Assert.assertFalse(_testCase.canExecute);
        Assert.assertFalse(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.ready:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertFalse(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.executing:
        Assert.assertFalse(_testCase.canExecute);
        Assert.assertFalse(_testCase.canReset);
        Assert.assertTrue(_testCase.canStop);
        break;
      case TestCaseStatus.exceptioned:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertTrue(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.unhandledExceptioned:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertTrue(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.stopped:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertTrue(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.failed:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertTrue(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      case TestCaseStatus.succeeded:
        Assert.assertTrue(_testCase.canExecute);
        Assert.assertTrue(_testCase.canReset);
        Assert.assertFalse(_testCase.canStop);
        break;
      default:
        var testCaseStatusName:String = _testCase.testCaseStatus ? _testCase.testCaseStatus.name : "null";
        throw new Error("Unhandled TestCaseStatus: " + testCaseStatusName);
    }
  }

  [After()]
  public function cleanup():void
  {
    _testCase = null;
  }
}
}
